package bjut.zrpc.core.optimization.beanPostProcessor;


import bjut.zrpc.core.optimization.annotation.ZRpcInterceptor;
import bjut.zrpc.core.optimization.annotation.ZRpcReference;
import bjut.zrpc.core.optimization.annotation.ZRpcSentinel;
import bjut.zrpc.core.optimization.annotation.ZRpcService;
import bjut.zrpc.core.optimization.bean.ZRpcServiceBean;
import bjut.zrpc.core.optimization.enums.ZRpcConfigEnum;
import bjut.zrpc.core.optimization.extension.ExtensionLoader;
import bjut.zrpc.core.optimization.factory.SingletonFactory;
import bjut.zrpc.core.optimization.interfaces.ServiceRegistry;
import bjut.zrpc.core.optimization.interfaces.ZRpcRequestTransport;
import bjut.zrpc.core.optimization.netty.NettyServer;
import bjut.zrpc.core.optimization.protect.LimitStream;
import bjut.zrpc.core.optimization.proxy.ProxyFactory;
import bjut.zrpc.core.optimization.proxy.ZRpcClientCglibProxy;
import bjut.zrpc.core.optimization.proxy.ZRpcClientJdkProxy;
import bjut.zrpc.core.optimization.utils.PropertiesReadUtil;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.concurrent.Semaphore;

/**
 * @Author: zhuzhiming
 * @Date: 2022/6/23 19:11
 */
@Component
public class ZRpcBeanPostProcessor implements BeanPostProcessor {

    private final ServiceRegistry serviceRegistry;
    private final ZRpcRequestTransport zRpcRequestTransport;
    private final NettyServer nettyServer;
    private static boolean isStart = true;

    public ZRpcBeanPostProcessor() {
        String registry = PropertiesReadUtil.getConfig(ZRpcConfigEnum.ZRPC_REGISTRY.getPropertyValue()[0]);
        String transport = PropertiesReadUtil.getConfig(ZRpcConfigEnum.ZRPC_TRANSPORTS.getPropertyValue()[0]);
        zRpcRequestTransport = ExtensionLoader.getExtensionLoader(ZRpcRequestTransport.class).getExtension(transport);
        serviceRegistry = ExtensionLoader.getExtensionLoader(ServiceRegistry.class).getExtension(registry);
        nettyServer = SingletonFactory.getInstance(NettyServer.class);
    }


    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        ZRpcService zRpcService = bean.getClass().getAnnotation(ZRpcService.class);
        if (zRpcService != null) {

            ZRpcServiceBean zRpcServiceBean = ZRpcServiceBean.builder()
                    .group(zRpcService.group())
                    .version(zRpcService.version())
                    .service(bean)
                    .timeout(zRpcService.timeout())
                    .inLimitTime(zRpcService.inTime())
                    .limit(zRpcService.limit())
                    .limitCount(zRpcService.limitCount())
                    .limitDuration(zRpcService.duration())
                    .concurrencyControl(zRpcService.concurrencyControl())
                    .concurrency(zRpcService.concurrency())
                    .semaphore(new Semaphore(zRpcService.concurrency()))
                    .build();

            ZRpcInterceptor zRpcInterceptor = bean.getClass().getAnnotation(ZRpcInterceptor.class);
            if (zRpcInterceptor != null) {
                String interceptor = zRpcInterceptor.interceptor();
                if (!"".equals(interceptor.trim())) {
                    zRpcServiceBean.setNeedInterceptor(true);
                    zRpcServiceBean.setInterceptor(interceptor);
                }
            }

            LimitStream.RPCSERVICE_MAP.put(zRpcServiceBean.getRpcServiceName(), zRpcServiceBean);

            serviceRegistry.registerService(zRpcServiceBean);

        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

        Field[] fields = bean.getClass().getDeclaredFields();
        for (Field field : fields) {
            ZRpcReference zRpcReference = field.getAnnotation(ZRpcReference.class);
            if (zRpcReference != null) {
                ZRpcServiceBean zRpcServiceBean = ZRpcServiceBean.builder()
                        .group(zRpcReference.group())
                        .version(zRpcReference.version())
                        .timeout(zRpcReference.timeout())
                        .increase(zRpcReference.increase())
                        .mock(zRpcReference.mock())
                        .mockMethod(zRpcReference.mockMethod())
                        .retry(zRpcReference.retry())
                        .build();
                ZRpcSentinel rpcSentinel = field.getAnnotation(ZRpcSentinel.class);
                if (rpcSentinel != null) {
                    zRpcServiceBean.setDuration(rpcSentinel.duration());
                    zRpcServiceBean.setSentinel(rpcSentinel.sentinel());
                    zRpcServiceBean.setExceptionCount(rpcSentinel.exceptionCount());
                    zRpcServiceBean.setInTime(rpcSentinel.inTime());
                }

                ProxyFactory rpcClientProxy;
                if (zRpcReference.proxy().equals("jdk")) {
                    rpcClientProxy = new ZRpcClientJdkProxy(zRpcRequestTransport, zRpcServiceBean);
                } else {
                    rpcClientProxy = new ZRpcClientCglibProxy(field.getType(), zRpcRequestTransport, zRpcServiceBean);
                }

                Object proxyBean = rpcClientProxy.createProxy(field.getType());

                field.setAccessible(true);
                try {
                    field.set(bean, proxyBean);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e.getMessage());
                }
            }
        }

        return bean;
    }
}
